#pragma once
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
#include <unistd.h>
#include "INETADDR.hpp"
#include "Log.hpp"
using namespace std;
using func = function<string(const string &, INETADDR&)>;
class UDPServer
{
public:
    UDPServer(uint16_t port, func fun)
        : _sockfd(-1), _port(port), _isrunning(false), _fun(fun)
    {
    }
    void Init()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd == -1)
        {
            LOG(LogLevel::FATAL) << "socket failed!";
            exit(1);
        }
        
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));

        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(_port);
        int n = bind(_sockfd, (struct sockaddr *)&server, sizeof(server));
        if (n != 0)
        {
            LOG(LogLevel::FATAL) << "bind failed!";
            close(_sockfd);
            exit(2);
        }
       
    }
    void Start()
    {
        _isrunning = true;
        struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t len = sizeof(client);

        while (_isrunning)
        {
            char buffer[1024];

            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&(client), &len);
            if (n == -1)
            {
                LOG(LogLevel::FATAL) << " recvfrom failed!";
                close(_sockfd);
                exit(3);
            }

           
            buffer[n] = '\0';
            string message = buffer;
            INETADDR peer(client);
            string result = _fun(message,peer);
            ssize_t m = sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)&client, sizeof(client));

            if (m == -1)
            {
                LOG(LogLevel::FATAL) << "sendto failed!";
                close(_sockfd);
                exit(4);
            }
           
        }
    }
    ~UDPServer()
    {
        close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _port;
    bool _isrunning;
    func _fun;
};
